#include "PlayState.h"
#include "NetworkState.h"

using namespace Ogre;
using namespace NxOgre;

PlayState PlayState::mPlayState;

PlayState::PlayState() :
	mRoot(0), mSceneMgr(0), mViewport(0), mDeviceManager(0), mGUIManager(0), mKeyboard(0), mMouse(0), mCamera(0),
	mDotSceneLoader(0),
	mChatBox(0), mSpriteManager(0),
	mQuit(false), mDismiss(false),
	nxWorld(0), nxScene(0),
	mMoveSpeed(100), mRotateSpeed(36), mMoveScale(0.0f), mRotScale(0.0f), mRotX(0), mRotY(0), mTranslateVector(Vector3::ZERO), mTranslateVector2(Vector3::ZERO),
	mDelay(0.0f)
{
	mWindow = 0;
	mDebugOverlay = 0;
	displayCameraDetails = false;
	statsOn = false;
	mTimeUntilNextToggle = 0.0f;
	mBulletTime1=0.0f;
	mBulletTime2=0.0f;
	victoryPlayed=false;
}

void PlayState::enter()
{
	// Assign variables
	mRoot = Root::getSingletonPtr();
	mWindow = mRoot->getAutoCreatedWindow();
	mSceneMgr = mRoot->getSceneManager("plitzScene");
	mDeviceManager = DeviceManager::getSingletonPtr();
	mDeviceManager->setBuffered(false);	// In-game uses unbuffered input
	mGUIManager = GUIManager::getSingletonPtr();
	mKeyboard = mDeviceManager->mKeyboard;
	mMouse = mDeviceManager->mMouse;

	// Set Animation Settings
	Animation::setDefaultInterpolationMode(Animation::IM_LINEAR);
    Animation::setDefaultRotationInterpolationMode(Animation::RIM_LINEAR);

	// Set up DotScene Loader
	mDotSceneLoader = new DotSceneLoader();
	
	// Chat Box and SpriteManager
	mChatBox = ChatBox::getSingletonPtr();
	Config::TEXTTIME = 3.0f;
	mSpriteManager=new Ogre2dManager;
	mSpriteManager->init(mSceneMgr, Ogre::RENDER_QUEUE_OVERLAY, true);

	// Reposition the chat box
	ChatBox::getSingletonPtr()->setPosition(0.34f,0.3f);

	// Event Flags
	mQuit = false;
	mDismiss = false;
	currRound = 0;
	mDelay = 0.0f;
	gameCompleted = 0;

	// NxOgre
	if (!nxWorld)
		nxWorld = new NxOgre::World();
	nxScene = nxWorld->createScene("nxScene", mSceneMgr, "gravity: yes, floor: no");
	
	// Set up Debug Overlay
	mDebugOverlay = OverlayManager::getSingleton().getByName("Core/DebugOverlay");

	// Register Quit Menu Dialog GUI Items
	mGUIManager->registerScene(mSceneMgr);
	mGUIManager->setGUISheet(CEGUI::WindowManager::getSingletonPtr()->getWindow("Quit/Sheet"));
	mGUIManager->hide(); // defaults to hidden
	setupEventHandlers();
	
	createScene();

	// Create Sound Manager
	soundManager = OgreAL::SoundManager::getSingletonPtr();
	
	// Create and Play music
	if (Config::LEVEL == "stonehenge.scene")
		PlayTheme = soundManager->createSound("PlayTheme", "StoneHenge.ogg", true);
	else 
		PlayTheme = soundManager->createSound("PlayTheme", "SpaceDock.ogg", true);
		
	PlayTheme->setGain(Config::BGSOUNDLEVEL);
	PlayTheme->play();

	GunShot = soundManager->createSound("GunShot", "GunShot.ogg", false);
	GunShot->setGain(Config::SOUNDFXLEVEL);
	PlayerDie = soundManager->createSound("PlayerDie", "PlayerDie.ogg", false);
	PlayerDie->setGain(Config::SOUNDFXLEVEL);
	Victory = soundManager->createSound("Victory", "Victory.ogg", false);
	Victory->setGain(Config::SOUNDFXLEVEL);

	mDelay = 8.0f;
	//ChatBox::getSingletonPtr()->addToBuffer("Welcome to Plizkrieg!");
}

void PlayState::exit()
{
	// Reset Event Flags
	mQuit = false;
	mDismiss = false;

	// Clearing physics and all its entities
	nxWorld->destroyScene("nxScene");
	Config::LEVEL = "";

	while (!ammunition.empty())
	{
		Bullet* temp = ammunition.back();
		ammunition.pop_back();
		delete temp;
	}
			
	// Removing Game GUI
	mChatBox->clear();
	mSpriteManager->end();
	delete mSpriteManager;

	// Removing Sound
	PlayTheme->stop();
	GunShot->stop();
	PlayerDie->stop();
	soundManager->destroySound(PlayTheme);
	soundManager->destroySound(GunShot);
	soundManager->destroySound(PlayerDie);
	soundManager->destroySound(Victory);

	PlayTheme = 0;
	GunShot = 0;
	PlayerDie = 0;	

	// Switch off debug overlays
	displayCameraDetails = false;
	statsOn = false;
	mDebugOverlay->hide();

	// Clean up
	delete mDotSceneLoader;
	mSceneMgr->clearScene();
	mSceneMgr->destroyAllCameras();
	mRoot->getAutoCreatedWindow()->removeAllViewports();

	delete player1;
	delete player2;
}

void PlayState::pause()
{
}

void PlayState::resume()
{
}

bool PlayState::keyPressed(const OIS::KeyEvent &arg)
{
	if (arg.key == OIS::KC_ESCAPE || arg.key == OIS::KC_C)
		mDismiss = true;
	if (arg.key == OIS::KC_Q)
		mQuit = true;

	return true;
}

bool PlayState::keyReleased(const OIS::KeyEvent &arg)
{
	return true;
}

bool PlayState::mouseMoved( const OIS::MouseEvent &arg )
{
	CEGUI::System::getSingleton().injectMouseMove( arg.state.X.rel, arg.state.Y.rel );
	mDeviceManager->mMouseCursor->updatePosition(arg.state.X.abs,arg.state.Y.abs);
	return true;
}

bool PlayState::mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
	CEGUI::System::getSingleton().injectMouseButtonDown(convertOISMouseButtonToCegui(id));
	return true;
}

bool PlayState::mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id )
{
	CEGUI::System::getSingleton().injectMouseButtonUp(convertOISMouseButtonToCegui(id));
	return true;
}

bool PlayState::frameStarted(const FrameEvent& evt)
{	
	// Update toggle buffering time
	if (mTimeUntilNextToggle >= 0)
		mTimeUntilNextToggle -= evt.timeSinceLastFrame;
		
	if (mBulletTime1 >= 0)
		mBulletTime1 -= evt.timeSinceLastFrame;

	if (mBulletTime2 >= 0)
		mBulletTime2 -= evt.timeSinceLastFrame;

	// Updating debug overlay stats
	updateStats();

	// Update chatbox
	mChatBox->loop(evt.timeSinceLastFrame);

	// Update GUI sprites
	drawSprites();

	/*if (firstRound)
	{
		mDelay = 3.0f;
		firstRound = false;
	}*/

	// Anything above delay checking is always rendered;
	// Anything below delay checking is frozen during game delay
	// Delay is set when the round is about to start
	if (mDelay > 0.0f)
	{
		// Update Sprite Display at the beginning of every round
		if (mDelay <= 1.0f)
			mSpriteManager->drawSprite("go.png",312,314,712,454);
		else if (mDelay <= 2.0f)
			mSpriteManager->drawSprite("set.png",312,314,712,454);
		else if (mDelay <= 3.0f)
			mSpriteManager->drawSprite("ready.png",312,314,712,454);
		mDelay -= evt.timeSinceLastFrame;
		return true;
	}
	else mDelay = 0.0f;

	if (gameCompleted == 0)
	{
		// Update bullets in game
		std::list<Bullet*>::iterator bulletItr = ammunition.begin();
		while (bulletItr != ammunition.end())
		{
			bool destroyed = (*bulletItr)->updateBullet(nxScene, player1, player2, evt);
			if (destroyed)
				bulletItr = ammunition.erase(bulletItr);
			else
				bulletItr++;
		}

		// Update players
		player1->update(evt, mKeyboard->isKeyDown(OIS::KC_W),mKeyboard->isKeyDown(OIS::KC_S));
		player2->update(evt, mKeyboard->isKeyDown(OIS::KC_UP),mKeyboard->isKeyDown(OIS::KC_DOWN));

		// Check for Victory
		if (player1->isDead() || player2->isDead())
		{
			if (player1->isDead())
			{
				player2->kills++;
				player1->deaths++;
				//mChatBox->addToBuffer("Get ready for the next round...");
			}
			if (player2->isDead())
			{
				player1->kills++;
				player2->deaths++;
				//mChatBox->addToBuffer("Get ready for the next round...");
			}

			if (currRound < Config::NUMOFROUNDS)
				resetLevel();
		}
	}

	// Capture input devices
	if (mKeyboard) mKeyboard->capture();
	if (mMouse)	mMouse->capture();

	if (!mDeviceManager->mUseBufferedInputMouse)
	{
		if (processUnbufferedMouseInput(evt) == false)
			return false;
	}

	if (!mDeviceManager->mUseBufferedInputKeys)
	{
		if (processUnbufferedKeyInput(evt) == false)
			return false;
	}

	return true;
}

bool PlayState::frameEnded(const FrameEvent& evt)
{
	if (mQuit)
		changeState(NetworkState::getInstance()); // Return to NET STATE
	if (mDismiss)
	{	
		mGUIManager->hide();
		mDeviceManager->setBuffered(false);
		mDismiss = false;
	}
	return true;
}

void PlayState::createScene()
{
	// Load the scene
	mDotSceneLoader->parseDotScene(Config::LEVEL,"Plitzkrieg",mSceneMgr, nxScene);

	// Create players
	player1 = new Player();
	player1->createPlayer(nxScene,1, P1SHOOTSTARTPOS);

	player2 = new Player();
	player2->createPlayer(nxScene,2, P2SHOOTSTARTPOS);
	
	// Grab default camera and assign viewport
	mCamera = mSceneMgr->getCamera("SD_Camera");
	mViewport = mRoot->getAutoCreatedWindow()->addViewport(mCamera);	
	mViewport->setBackgroundColour(ColourValue(0.0, 0.0, 0.0));
	mCamera->setAspectRatio(Real(mViewport->getActualWidth()) / Real(mViewport->getActualHeight()));
}

bool PlayState::processUnbufferedMouseInput(const FrameEvent& evt)
{
	using namespace OIS;

	const MouseState &ms = mMouse->getMouseState();

	// Track mouse movement
	CEGUI::System::getSingleton().injectMouseMove(ms.X.rel,ms.Y.rel);
	mDeviceManager->mMouseCursor->updatePosition(ms.X.abs,ms.Y.abs);
	
	return true;
}

bool PlayState::processUnbufferedKeyInput(const FrameEvent& evt)
{
	using namespace OIS;

	// Summon quit menu
	if (mKeyboard->isKeyDown(KC_ESCAPE))
	{
		if (gameCompleted != 0) // allow the user to hit esc to go back
		{
			mQuit = true;
			return true;
		}
		if (!mGUIManager->isDisplayed())
		{
			mGUIManager->show();
			mDeviceManager->setBuffered(true);
		}
	}

	// Only allow game keys if game not completed
	if (gameCompleted == 0)
	{
		// PLAYER 1 MOVEMENT CONTROLS //
		if (mKeyboard->isKeyDown(KC_W))
			player1->keyPressed(Player::UP);
		if (mKeyboard->isKeyDown(KC_S))
			player1->keyPressed(Player::DOWN);
		if (mKeyboard->isKeyDown(KC_A))
			player1->keyPressed(Player::LEFT);
		if (mKeyboard->isKeyDown(KC_D))
			player1->keyPressed(Player::RIGHT);
		//if (mKeyboard->isKeyDown(OIS::KC_LMENU))
		if (mKeyboard->isKeyDown(OIS::KC_B))
			player1->keyPressed(Player::JUMP);

		// PLAYER 1 SHOOTING CONTROLS //
		//if (mKeyboard->isKeyDown(KC_LSHIFT) && mTimeUntilNextToggle <= 0)
		if (mKeyboard->isKeyDown(OIS::KC_V) && mBulletTime1 <= 0)
		{
			if (PlayerDie->isPlaying())
				PlayerDie->stop();
			if (GunShot->isPlaying())
				GunShot->stop();
			GunShot->play();
			Bullet* temp = new Bullet(nxScene,player1);
			ammunition.push_back(temp);
			mBulletTime1 = 0.5f;
		}

		// PLAYER 2 CONTROLS
		if (mKeyboard->isKeyDown(OIS::KC_UP))
			player2->keyPressed(Player::UP);
		if (mKeyboard->isKeyDown(OIS::KC_DOWN))
			player2->keyPressed(Player::DOWN);
		if (mKeyboard->isKeyDown(OIS::KC_LEFT))
			player2->keyPressed(Player::LEFT);
		if (mKeyboard->isKeyDown(OIS::KC_RIGHT))
			player2->keyPressed(Player::RIGHT);
		if (mKeyboard->isKeyDown(OIS::KC_SLASH))
			player2->keyPressed(Player::JUMP);

		// PLAYER 2 SHOOTING CONTROLS //
		//if (mKeyboard->isKeyDown(KC_SPACE) && mTimeUntilNextToggle <= 0)
		if (mKeyboard->isKeyDown(OIS::KC_PERIOD) && mBulletTime2 <= 0)
		{
			if (PlayerDie->isPlaying())
				PlayerDie->stop();
			if (GunShot->isPlaying())
				GunShot->stop();
			GunShot->play();
			Bullet* temp = new Bullet(nxScene,player2);
			ammunition.push_back(temp);
			mBulletTime2 = 0.5f;
		}
	}
	
	// Toggle Debug Overlay
	if (mKeyboard->isKeyDown(KC_F1) && mTimeUntilNextToggle <= 0 && Config::getInstance()->cfg_debugOn)
	{
		statsOn = !statsOn;
		mTimeUntilNextToggle = 0.5;
		if (statsOn) mDebugOverlay->show();
		else mDebugOverlay->hide();
	}
	
	// Take Screen Shot
	if (mKeyboard->isKeyDown(KC_SYSRQ) && mTimeUntilNextToggle <= 0)
	{
		mWindow->writeContentsToTimestampedFile("Plitzkrieg",".jpg");
		mTimeUntilNextToggle = 0.5;
	}

	// Render in wireframe
	if (mKeyboard->isKeyDown(KC_R) && mTimeUntilNextToggle <=0)
	{
		if (mCamera->getPolygonMode() == PM_SOLID)
			mCamera->setPolygonMode(PM_WIREFRAME);
		else
			mCamera->setPolygonMode(PM_SOLID);
				
		mTimeUntilNextToggle = 0.5;
	}
	
	// Show Camera Pos in Debug Overlay
	if (mKeyboard->isKeyDown(KC_P) && mTimeUntilNextToggle <= 0)
	{
		displayCameraDetails = !displayCameraDetails;
		mTimeUntilNextToggle = 0.5;
		if (!displayCameraDetails)
			mDebugText = "";
	}
	if (displayCameraDetails)
	{
		// Print camera details
		//mDebugText = "P: " + StringConverter::toString(mCamera->getDerivedPosition()) +
		//	" " + "O: " + StringConverter::toString(mCamera->getDerivedOrientation());
		mDebugText = "P1: " + StringConverter::toString(player1->health) + " " + "P2: " +
			StringConverter::toString(player2->health);
	}

	return true;
}

void PlayState::drawSprites()
{
	// Draw Player GUI
	// Draw based on players' current HP
	mSpriteManager->drawSprite("healthbar_left.png",30,664,291,758);
	mSpriteManager->drawSprite("healthbar_right.png",731,664,992,758);

	int leftBloodLevel = player1->health/100*157;
	if (leftBloodLevel <= 0) leftBloodLevel = 0;
	int rightBloodLevel = player2->health/100*157;
	if (rightBloodLevel <= 0) rightBloodLevel = 0;

	mSpriteManager->drawSprite("healthbar_blood.png",30+90,664+50,30+90+leftBloodLevel,664+50+12);
	mSpriteManager->drawSprite("healthbar_blood.png",992-90-rightBloodLevel,664+50,992-90,664+50+12);

	// Draw player medals
	for (int i=0; i < player1->kills; i++)
		mSpriteManager->drawSprite("redmedal.png",120+i*30,654,120+(i+1)*30,704);
	for (int i=0; i < player2->kills; i++)
		mSpriteManager->drawSprite("bluemedal.png",994-120-i*30,654,1024-120-(i*30),704);

	if (gameCompleted == 1){
		mSpriteManager->drawSprite("redwin.png",312,314,712,454);
		if (PlayTheme->isPlaying())
			PlayTheme->stop();
		if (GunShot->isPlaying())
			GunShot->stop();
		if (PlayerDie->isPlaying())
			PlayerDie->stop();
		if (!victoryPlayed){
			Victory->play();	
			victoryPlayed=true;
		}
	}
	if (gameCompleted == 2){
		mSpriteManager->drawSprite("bluewin.png",312,314,712,454);
		if (PlayTheme->isPlaying())
			PlayTheme->stop();
		if (GunShot->isPlaying())
			GunShot->stop();
		if (PlayerDie->isPlaying())
			PlayerDie->stop();
		if (!victoryPlayed){
			Victory->play();
			victoryPlayed=true;
		}
	}
	if (gameCompleted == 3){
		mSpriteManager->drawSprite("drawgame.png",312,314,712,454);
		if (PlayTheme->isPlaying())
			PlayTheme->stop();
		if (GunShot->isPlaying())
			GunShot->stop();
		if (PlayerDie->isPlaying())
			PlayerDie->stop();
		if (!victoryPlayed){
			Victory->play();
			victoryPlayed=true;
		}
	}
}

void PlayState::resetLevel()
{
	if (GunShot->isPlaying())
		GunShot->stop();
	if (PlayerDie->isPlaying())
		PlayerDie->stop();
	PlayerDie->play();

	// Increment round counter
	currRound++;
	
	// Reset player positions
	player1->reset();
	player2->reset();
	mDelay = 3.0f;
	//mSpriteTimer = 0.0;

	// Remove all bullets in play
	std::list<Bullet*>::iterator bulletItr = ammunition.begin();
	while (bulletItr != ammunition.end())
	{
		nxScene->destroyBody((*bulletItr)->getName());
		bulletItr = ammunition.erase(bulletItr);
	}

	// Check for game victory
	if (player1->kills > Config::NUMOFROUNDS/2)
	{
		gameCompleted = 1;	// player 1 wins
		mDelay = 0.0f;
	}
	else if (player2->kills > Config::NUMOFROUNDS/2)
	{
		gameCompleted = 2;	// player 2 wins
		mDelay = 0.0f;
	}
	else if (currRound == Config::NUMOFROUNDS)
	{
		gameCompleted = 3;	// draw game
		mDelay = 0.0f;
	}
}

void PlayState::setupEventHandlers()
{
	CEGUI::WindowManager *wmgr = CEGUI::WindowManager::getSingletonPtr();
	CEGUI::Window *quit = wmgr->getWindow((CEGUI::utf8*)"Quit/QuitButton");
	quit->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&PlayState::handleQuit, this));
    CEGUI::Window *cancel = wmgr->getWindow((CEGUI::utf8*)"Quit/CancelButton");
	cancel->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber(&PlayState::handleCancel, this));
}

bool PlayState::handleQuit(const CEGUI::EventArgs& e)
{
	mQuit = true;
	return true;
}

bool PlayState::handleCancel(const CEGUI::EventArgs& e)
{
	mDismiss = true;
	return true;
}
